/**
 * 
 */
package info.niwota.pasta.net;

import info.niwota.pasta.Debug;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.BindException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Random;

import android.util.Log;

/**
 * Import server
 * 
 * @author qiangli
 *
 */
public class Server extends Thread {

	private static final String TAG = "Server";

	protected ServerSocket socket = null;

	protected boolean running = false;

	private Callback cb;

	public Server(Callback cb) throws Exception {
		this.socket = createServerSocket();
		this.cb = cb;
	}

	public void run() {
		if (socket == null) {
			throw new RuntimeException("socket not initialized");
		}
		running = true;
		
		setName("SocketRedirect");
		try {
			while (running) {
				
				if (Debug.DEBUG) {
					Log.d(TAG, socket + " listening: " + socket.getLocalSocketAddress());
				}
				
				Socket in = socket.accept();
				
				if (Debug.DEBUG) {
					Log.d(TAG, socket + " accepted: "+ in.getRemoteSocketAddress() + ": " + in.getPort());
				}
				
				handle(in);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		running = false;
		
		try {
			socket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (Debug.DEBUG) {
			Log.d(TAG, socket + " running: " + running);
		}	
	}

	private void handle(Socket s) throws Exception {
		ObjectInputStream in = new ObjectInputStream(s.getInputStream());
		ObjectOutputStream out = new ObjectOutputStream(s.getOutputStream());
		String data = (String) in.readObject();
		cb.handle(data);
		out.writeObject("OK");
		out.close();
		in.close();
	}

	protected void close(Socket s) {
		if (s == null) {
			return;
		}
		try {
			s.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void cancel() {
		running = false;
		try {
			super.interrupt();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public boolean isRunning() {
		return running;
	}
	
	public int getPort() {
		return socket.getLocalPort();
	}
	
//	public String getAddress() {
//		return socket.getInetAddress().getHostAddress();
//	}

	public static ServerSocket createServerSocket() throws Exception {
		//port also meant as a password, so we start from a random number
		Random rand = new Random();	
		final int min = 1024 + rand.nextInt(1000);
		final int max = Integer.MAX_VALUE;
		for (int p = min; p < max; p++) {
			try {
				ServerSocket ss = new ServerSocket(p);
				return ss;
			} catch (BindException e) {
				continue;
			} catch (IOException e) {
				throw e;
			}
		}
		return null;
	}
	
//	public static String getAddress(Context ctx) {
//		WifiManager wifi = (WifiManager) ctx.getSystemService(Context.WIFI_SERVICE);
//		WifiInfo wifiInfo = wifi.getConnectionInfo();
//		int ip = wifiInfo.getIpAddress();
//		return Formatter.formatIpAddress(ip);
//	}
//	
	public String getAddress() throws Exception {
		for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
            NetworkInterface intf = en.nextElement();
            for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                InetAddress inetAddress = enumIpAddr.nextElement();
                if (!inetAddress.isLoopbackAddress()) {
                    return inetAddress.getHostAddress().toString();
                }
            }
        }
		return null;
	}
}
